﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Runtime.Serialization;
using System.Threading.Tasks;
using MishraReader.Entities.Tools;

namespace MishraReader.Entities
{
    [DataContract]
    [DebuggerDisplay("Unread = {UnreadsCount}, Subscriptions = {_subscriptions.Count}, Categories = {_categories.Count}")]
    public class SubscriptionsList : Notifier
    {
        public const string NoCategoryId = "__";
        public readonly Object SyncRoot = new object();

        private List<SubscriptionCategory> _categories;

        private List<PropertyObserver<Subscription>> _observers;

        private AsyncLock _actionLock;

        [DataMember(Name = "subscriptions")]
        private List<Subscription> _subscriptions;

        public SubscriptionsList()
        {
            _subscriptions = new List<Subscription>(); // placeholder as WCF will never call the ctor

            OnDeserialized(default(StreamingContext));
        }

        public Task<AsyncLock.Releaser> LockAsync([CallerMemberName] string callingMethod = null, [CallerFilePath] string path = null, [CallerLineNumber] int line = 0)
        {
           // Debug.WriteLine("SubscriptionList.LockAsync called by: " + callingMethod + " in file: " + path + " : " + line);

            return _actionLock.LockAsync(callingMethod, path, line);
        }

        public IEnumerable<SubscriptionCategory> Categories
        {
            get { return _categories; }
        }
        
        public IEnumerable<Subscription> Subscriptions
        {
            get { return _subscriptions; }
        }


        public int UnreadsCount
        {
            get { return Subscriptions.Sum(s => s.UnreadsCount); }
        }
        

        private void RaiseUnreadsCountChanged()
        {
            RaisePropertyChanged(() => UnreadsCount);
        }

        internal void ProcessCategories(Subscription subscription)
        {
            var subscriptionsList = subscription.SubscriptionsList;

            if (subscription.Category == null)
                return;

            foreach (var category in subscription.Categories.ToList())
            {
                subscriptionsList.AddCategory(category);

                var obs = PropertyObserver.Create(subscription);
                _observers.Add(obs);
                obs.RegisterHandler(s => s.UnreadsCount, _ => RaiseUnreadsCountChanged());


                var localCategory = subscriptionsList.Categories.First(s => s.Equals(category));
                subscription.SetLocalCategory(localCategory);

                if (!localCategory.Subscriptions.Contains(subscription))
                    localCategory.AddSubscription(subscription);
            }
        }

        private void AddCategory(SubscriptionCategory category)
        {
            if (Categories.Contains(category))
                return;

            _categories.Add(category);
        }

        [OnDeserialized]
        private void OnDeserialized(StreamingContext c)
        {
            _categories = new List<SubscriptionCategory>();
            _observers = new List<PropertyObserver<Subscription>>();
            _actionLock = new AsyncLock();
        }

        public void FinalizeInit()
        {
            var nocat = Categories.FirstOrDefault(c => c.Id == NoCategoryId);
            if (nocat != null)
            {
                foreach (var sub in nocat.Subscriptions)
                {
                    // Create a category for this sub at the end
                    var cat = new SubscriptionCategory(this)
                        {
                            Id = sub.Id,
                            Label = sub.Title,
                            UnreadCount = sub.ServerUnreadsCount,
                            IsFeed = true
                        };

                    cat.AddSubscription(sub);

                    AddCategory(cat);
                }
            }
        }
    }
}